Skip to content

Adding animation screenshot tests#4821

Merged
shai-almog merged 12 commits intomasterfrom
animation-screenshot-tests
Apr 30, 2026
Merged

Adding animation screenshot tests#4821
shai-almog merged 12 commits intomasterfrom
animation-screenshot-tests

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

No description provided.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented Apr 28, 2026

Compared 82 screenshots: 82 matched.

Native Android coverage

  • 📊 Line coverage: 9.54% (5157/54084 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.50% (25382/338441), branch 3.45% (1120/32488), complexity 4.43% (1377/31116), method 7.75% (1126/14537), class 12.76% (248/1944)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.54% (5157/54084 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.50% (25382/338441), branch 3.45% (1120/32488), complexity 4.43% (1377/31116), method 7.75% (1126/14537), class 12.76% (248/1944)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1020.000 ms
Base64 CN1 encode 287.000 ms
Base64 encode ratio (CN1/native) 0.281x (71.9% faster)
Base64 native decode 973.000 ms
Base64 CN1 decode 227.000 ms
Base64 decode ratio (CN1/native) 0.233x (76.7% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented Apr 28, 2026

Compared 82 screenshots: 82 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 200 seconds

Build and Run Timing

Metric Duration
Simulator Boot 95000 ms
Simulator Boot (Run) 1000 ms
App Install 14000 ms
App Launch 9000 ms
Test Execution 286000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1507.000 ms
Base64 CN1 encode 1504.000 ms
Base64 encode ratio (CN1/native) 0.998x (0.2% faster)
Base64 native decode 876.000 ms
Base64 CN1 decode 1259.000 ms
Base64 decode ratio (CN1/native) 1.437x (43.7% slower)
Base64 SIMD encode 471.000 ms
Base64 encode ratio (SIMD/native) 0.313x (68.7% faster)
Base64 encode ratio (SIMD/CN1) 0.313x (68.7% faster)
Base64 SIMD decode 432.000 ms
Base64 decode ratio (SIMD/native) 0.493x (50.7% faster)
Base64 decode ratio (SIMD/CN1) 0.343x (65.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 82.000 ms
Image createMask (SIMD on) 15.000 ms
Image createMask ratio (SIMD on/off) 0.183x (81.7% faster)
Image applyMask (SIMD off) 176.000 ms
Image applyMask (SIMD on) 74.000 ms
Image applyMask ratio (SIMD on/off) 0.420x (58.0% faster)
Image modifyAlpha (SIMD off) 131.000 ms
Image modifyAlpha (SIMD on) 69.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.527x (47.3% faster)
Image modifyAlpha removeColor (SIMD off) 214.000 ms
Image modifyAlpha removeColor (SIMD on) 73.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.341x (65.9% faster)
Image PNG encode (SIMD off) 1515.000 ms
Image PNG encode (SIMD on) 1217.000 ms
Image PNG encode ratio (SIMD on/off) 0.803x (19.7% faster)
Image JPEG encode 809.000 ms

@shai-almog shai-almog linked an issue Apr 29, 2026 that may be closed by this pull request
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented Apr 29, 2026

✅ JavaScript-port screenshot tests passed.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented Apr 29, 2026

Compared 7 screenshots: 7 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

shai-almog and others added 4 commits April 29, 2026 18:14
BaseTest defaults to a 1500ms UITimer between Form.onShowCompleted and
the screenshot emission so Android has time to settle real form
contents before the capture fires. The animation/transition/replace/
scroll tests render entirely off-screen into an Image and don't depend
on the host form's contents, so the long settle wait just costs
runtime budget.

iOS CI's run-ios-ui-tests.sh enforces a 300s end-marker deadline. The
last successful run before this branch grew to 17 animation tests took
276s; the 17 extra 1500ms waits were the difference between a 25s
margin and the most recent run's STAGE:TIMEOUT. Dropping to 200ms
saves ~22s and leaves comfortable headroom.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ably

The Cn1ssChunkTools gap-detection added in 963dd5a correctly fails any
PNG capture whose base64 chunk stream has missing offsets. The JavaScript
port's console.log channel routinely drops mid-line under logcat-style
truncation, so every screenshot-emitting test on JS now produces an
incomplete stream and trips the new validation. The validation is doing
its job - dropped chunks have always been a real bug, the old lenient
decoder just hid them by silently concatenating whatever survived.

Until the JS port emits chunks reliably (separate PR), add every test in
the latest JS run's "decode failures" list to the HTML5 skip set so the
JS step doesn't block the PR. The skip set is also refactored from a
chained `||` boolean expression into an `Arrays.asList`-populated
`HashSet` since the entry count is past the point where the chain stays
readable.

Tests added: KotlinUiTest, MainScreenScreenshotTest, the eight other
named screenshot tests (Sheet, ImageViewerNavigation, Tabs,
TextAreaAlignment, ToastBarTopPosition, ValidatorLightweightPicker,
LightweightPickerButtons), and all 25 graphics tests. iOS/Android
continue running and validating these tests, so dropped chunks still
surface there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revert the dynamic test-array builder and the static HashSet skip set;
both relied on static field initializers that called either
Display.getInstance() (in DEFAULT_TEST_CLASSES = buildDefaultTestClasses())
or built collections via Arrays.asList/HashSet (in HTML5_SKIP_TESTS),
and both broke iOS class loading. The iOS device-runner.log on every
run since be4f0e4 contained only the SWIFT_DIAG messages, then
nothing - Cn1ssDeviceRunner failed to load before runSuite() could log
a single starting test=... entry, leaving the suite to time out at the
300s end-marker deadline.

DEFAULT_TEST_CLASSES is back to a plain `new BaseTest[]{...}` literal
and shouldForceTimeoutInHtml5 is back to inline `||` chains, split
across four small static helpers (native APIs, theme tests, animation
tests, JS-broken screenshot tests) so the readability cost of carrying
~70 entries stays manageable. No static field initialization that
involves method calls; class loads cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The JS port's lambdaBridge consults cn1ssForcedTimeoutTestClasses /
cn1ssForcedTimeoutTestNames in port.js BEFORE calling Java's
shouldForceTimeoutInHtml5 (per the comment on commit 3edb800). Tests
not registered in those maps run for real on JS, hit the chunk-drop
gap detection or the per-test timeout, and the suite then exceeds the
150s browser lifetime budget. The JS run on 421606b was finishing
ComponentReplaceSlideScreenshotTest 30s before the deadline marker
because all 17 animation tests + the 9 named screenshot tests + the
25 graphics tests were still being executed.

Add every entry from Cn1ssDeviceRunner's Java-side skip helpers to
both port.js maps so the JS port force-finalises them at lambda
dispatch time without ever calling into Java's runTest.

iOS/Android still run all of these and the chunk-stream gap detector
still surfaces real drops there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

shai-almog and others added 2 commits April 29, 2026 22:13
After registering every chunk-emitting test in port.js's
cn1ssForcedTimeoutTestClasses (5030308), the JS suite legitimately
finishes with LOG_CHUNKS=0 - every screenshot test is force-finalised
before runTest emits anything. The script's pre-existing
MARKERS_NOT_FOUND check treats zero chunks as a fatal "harness never
ran" condition and exits 12, blocking the CI step on what is actually
a clean end-to-end run.

If CN1SS:SUITE:FINISHED is present in the browser log, the harness
clearly did run; treat that as a successful no-screenshot pass and
skip the rest of the decode/compare pipeline (there's nothing to
compare). The MARKERS_NOT_FOUND failure path still triggers when the
suite genuinely never ran.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Android JDK 21 build job flaked twice in a row on this branch with
the new chunk-gap validation (added in 963dd5a) catching a single
"PNG chunk truncated before CRC" per run on a different test each run:
SlideHorizontalTransitionTest on the 5030308 run, MultiButtonTheme_dark
on the 57b3de1 run. JDK 17 and Default (8) pass cleanly each time.

The 20ms throttle was tuned in 763bd66 (#4253) against the smaller
screenshot suite. With 17 new animation tests each emitting ~150KB
PNGs (~400 chunks each) on top of the 13 theme captures, total
chunked log volume per run roughly doubled, and JDK 21's logcat drains
slower under that load. 30ms gives logcat ~50% more drain time per
chunk; emission cost grows linearly so the per-test overhead is small
compared to a flaky CI re-run.

The strict gap validation in Cn1ssChunkTools stays on iOS/Android as a
real defect detector - this just stops it tripping on transient JDK 21
logcat backpressure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 644 total, 0 failed, 2 skipped

Benchmark Results

  • Execution Time: 11083 ms

  • Hotspots (Top 20 sampled methods):

    • 26.75% java.util.ArrayList.indexOf (517 samples)
    • 23.18% java.lang.String.indexOf (448 samples)
    • 17.43% com.codename1.tools.translator.Parser.isMethodUsed (337 samples)
    • 4.09% java.lang.Object.hashCode (79 samples)
    • 2.85% java.lang.System.identityHashCode (55 samples)
    • 1.97% com.codename1.tools.translator.ByteCodeClass.updateAllDependencies (38 samples)
    • 1.35% com.codename1.tools.translator.ByteCodeClass.markDependent (26 samples)
    • 1.29% com.codename1.tools.translator.ByteCodeClass.calcUsedByNative (25 samples)
    • 1.29% java.lang.StringBuilder.append (25 samples)
    • 1.03% com.codename1.tools.translator.BytecodeMethod.appendMethodSignatureSuffixFromDesc (20 samples)
    • 0.93% com.codename1.tools.translator.Parser.cullMethods (18 samples)
    • 0.88% com.codename1.tools.translator.BytecodeMethod.optimize (17 samples)
    • 0.78% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (15 samples)
    • 0.62% com.codename1.tools.translator.BytecodeMethod.isMethodUsedByNative (12 samples)
    • 0.62% org.objectweb.asm.ClassReader.readCode (12 samples)
    • 0.57% sun.nio.ch.FileDispatcherImpl.write0 (11 samples)
    • 0.57% com.codename1.tools.translator.ByteCodeClass.isDefaultInterfaceMethod (11 samples)
    • 0.52% com.codename1.tools.translator.ByteCodeField.equals (10 samples)
    • 0.52% com.codename1.tools.translator.Parser.getClassByName (10 samples)
    • 0.47% java.util.TreeMap.getEntry (9 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@shai-almog shai-almog merged commit 2791693 into master Apr 30, 2026
20 of 25 checks passed
liannacasper pushed a commit that referenced this pull request Apr 30, 2026
The animation/transition test suite (added in #4821) creates 7 mutable
images per test × ~17 tests, exhausting Metal device memory mid-suite
and hanging the runner after the third test. Root cause: GLUIImage's
Metal ivars and CN1Metalcompat's lazy texture caches all held +1 retains
that no one was releasing.

Fixes:
- GLUIImage.dealloc releases mtlTexture, mtlMutableTexture and
  mtlMutableCommandBuffer (was nil-out only).
- setMtlMutableTexture / setMtlMutableCommandBuffer now retain incoming
  values and release the previous slot. Without retain the autoreleased
  command buffer from [queue commandBuffer] would dangle after the next
  pool drain.
- setImage: releases the cached mtlTexture before nil-ing it.
- CN1MetalEnsureMutableTexture releases the +1 from
  newTextureWithDescriptor: now that the setter retains.
- CN1MetalReadMutableImagePixels releases the staging "shared" texture
  after copy-out (previously leaked one full-resolution texture per
  Image.getRGB call).
- Text and gradient caches release the evicted slot's +1 retains before
  overwriting (text cache also evicts a leaked NSString key per slot
  rotation).

Same MRR ownership pattern as CN1MetalGlyphAtlas (b9c5add / 4de8cb0).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liannacasper pushed a commit that referenced this pull request Apr 30, 2026
Reinstates the drain-and-wait + Metal blit path disabled in 9b2aaf1
("disable step-6 readback (suspected hang in DrawImage)"). The legacy
fallback returns the stale UIImage initial-fill for any mutable that's
been drawn into post-Phase 3, which was tolerable until master #4821
landed the animation/transition test suite — those tests build screenshots
by reading back from per-frame mutables via Image.getRGB and PNG-encode,
so the disabled path means every animation grid emits empty pixels and
downstream EDT scheduling stalls on the empty-result follow-up.

Original GPU-pressure deadlock concern (nextDrawable wedge) is mitigated
by the resource-leak fix in e548d1a: GLUIImage no longer pins +1
retains on its mtlMutableTexture / mutable command buffer / shared
readback texture, so transient mutables can release back to the device
between frames.

Bracketed with finishDrawingOnImageImpl so an in-progress draw session
is closed before the drain (queued ops drained via flushBuffer's
dispatch_sync(main, drawFrame)) and reopened after the blit-and-read,
mirroring the legacy fallback's pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liannacasper pushed a commit that referenced this pull request Apr 30, 2026
PNG/JPEG encoding flows through ImageIO.save → createImageFile, which
calls [GLUIImage getImage] and encodes the original UIImage backing.
For any mutable image that has been drawn into via the Phase 3 v2 Metal
pipeline that backing is just the initial fill — so emitImage on the
animation/transition tests added in master #4821 was producing black
grids regardless of what got painted into the frames.

Drain the op queue (outside the dispatch_sync wrapper, to avoid nested
dispatch_sync to main) so the mutable's MTLTexture is up-to-date, then
inside the dispatch_sync block blit-and-read those pixels into a fresh
UIImage and encode that. Matches the imageRgbToIntArrayImpl path
re-enabled in the previous commit.

If the JNI is invoked while a draw-on-image session is open against
this peer, finishDrawing is called before the drain and reopened
afterwards so the surrounding Java code observes a continuous session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Apr 30, 2026
…ridge

PR #4821 added 124 lines to Cn1ssDeviceRunner.java, which shifted the
translator's lambda numbering: `lambda_awaitTestCompletion_3_*` became
`_0_`, and `lambda_finalizeTest_4_*` became `_0_`. port.js still
referenced the old `_3_` / `_4_` IDs hardcoded.

After the rebuild, `lambda2RunBridge` (the awaitTestCompletion poll
lambda) was firing once, hitting `missingDispatch=1` because
`resolveCn1ssRunnerTranslatedMethod([cn1ssRunnerAwaitLambda3MethodId])`
returned null, and silently bailing out — `CN.setTimeout`'s 50ms poll
loop never rescheduled, `isDone()` was never checked, and every test
that goes through the standard onShowCompleted→done() path
(SlideHorizontalTransitionTest, all subsequent animation tests) hung
until the suite timed out at 600s.

Smoking gun in the browser log:

  lambda3RunBridge:dispatch:index=1:nextIndex=2     (MainScreen finalized)
  lambda2RunBridge:HIT                              (Slide poll fires once)
  lambda2RunBridge:missingDispatch=1                (lookup fails, polling dies)

Build the candidate-id list by looping 0..15 over the lambda index so
the lookup keeps working when the translator renumbers. Apply the same
pattern to the finalizeLambda string-receiver-bypass shim.

Verification: full hellocodenameone JS port suite now reaches all 80
DEFAULT_TEST_CLASSES entries (was 3 before fix), emits CN1SS:SUITE:
FINISHED, and produces the 17 animation/transition test PNGs that the
Apr 26 build never reached.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liannacasper pushed a commit that referenced this pull request Apr 30, 2026
…ridge

PR #4821 added 124 lines to Cn1ssDeviceRunner.java, which shifted the
translator's lambda numbering: `lambda_awaitTestCompletion_3_*` became
`_0_`, and `lambda_finalizeTest_4_*` became `_0_`. port.js still
referenced the old `_3_` / `_4_` IDs hardcoded.

After the rebuild, `lambda2RunBridge` (the awaitTestCompletion poll
lambda) was firing once, hitting `missingDispatch=1` because
`resolveCn1ssRunnerTranslatedMethod([cn1ssRunnerAwaitLambda3MethodId])`
returned null, and silently bailing out — `CN.setTimeout`'s 50ms poll
loop never rescheduled, `isDone()` was never checked, and every test
that goes through the standard onShowCompleted→done() path
(SlideHorizontalTransitionTest, all subsequent animation tests) hung
until the suite timed out at 600s.

Smoking gun in the browser log:

  lambda3RunBridge:dispatch:index=1:nextIndex=2     (MainScreen finalized)
  lambda2RunBridge:HIT                              (Slide poll fires once)
  lambda2RunBridge:missingDispatch=1                (lookup fails, polling dies)

Build the candidate-id list by looping 0..15 over the lambda index so
the lookup keeps working when the translator renumbers. Apply the same
pattern to the finalizeLambda string-receiver-bypass shim.

Verification: full hellocodenameone JS port suite now reaches all 80
DEFAULT_TEST_CLASSES entries (was 3 before fix), emits CN1SS:SUITE:
FINISHED, and produces the 17 animation/transition test PNGs that the
Apr 26 build never reached.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liannacasper pushed a commit that referenced this pull request Apr 30, 2026
…ridge

PR #4821 added 124 lines to Cn1ssDeviceRunner.java, which shifted the
translator's lambda numbering: `lambda_awaitTestCompletion_3_*` became
`_0_`, and `lambda_finalizeTest_4_*` became `_0_`. port.js still
referenced the old `_3_` / `_4_` IDs hardcoded.

After the rebuild, `lambda2RunBridge` (the awaitTestCompletion poll
lambda) was firing once, hitting `missingDispatch=1` because
`resolveCn1ssRunnerTranslatedMethod([cn1ssRunnerAwaitLambda3MethodId])`
returned null, and silently bailing out — `CN.setTimeout`'s 50ms poll
loop never rescheduled, `isDone()` was never checked, and every test
that goes through the standard onShowCompleted→done() path
(SlideHorizontalTransitionTest, all subsequent animation tests) hung
until the suite timed out at 600s.

Smoking gun in the browser log:

  lambda3RunBridge:dispatch:index=1:nextIndex=2     (MainScreen finalized)
  lambda2RunBridge:HIT                              (Slide poll fires once)
  lambda2RunBridge:missingDispatch=1                (lookup fails, polling dies)

Build the candidate-id list by looping 0..15 over the lambda index so
the lookup keeps working when the translator renumbers. Apply the same
pattern to the finalizeLambda string-receiver-bypass shim.

Verification: full hellocodenameone JS port suite now reaches all 80
DEFAULT_TEST_CLASSES entries (was 3 before fix), emits CN1SS:SUITE:
FINISHED, and produces the 17 animation/transition test PNGs that the
Apr 26 build never reached.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create an animation based screenshot test

1 participant